package com.yupi.springbootinit.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.manager.BigModelNew;
import com.yupi.springbootinit.mapper.ChartMapper;
import com.yupi.springbootinit.mapper.DynamicChartMapper;
import com.yupi.springbootinit.model.dto.chart.ChartDto;
import com.yupi.springbootinit.model.dto.chart.ChartQueryRequest;
import com.yupi.springbootinit.model.entity.Chart;
import com.yupi.springbootinit.model.entity.DynamicChart;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.vo.ChartVO;
import com.yupi.springbootinit.service.ChartService;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.GetSql;
import com.yupi.springbootinit.utils.SqlUtils;
import com.yupi.springbootinit.utils.StrToMap;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.yupi.springbootinit.constant.RedisConstants.*;

/**
 *
 */
@Service
public class ChartServiceImpl extends ServiceImpl<ChartMapper, Chart> implements ChartService{

   @Resource
   private BigModelNew bigModelNew;

   @Resource
   private DynamicChartMapper dynamicChartMapper;

   @Resource
   private UserService userService;

   @Resource
   private ChartMapper chartMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    @Transactional
    public boolean updateChartById(Chart chart,HttpServletRequest request) throws Exception {
        //获取用户修改后的csv数据(chartData)
        String chartData = chart.getChartData();
        String chartType = chart.getChartType();
        String userGoal = chart.getGoal();
        String tableName = "chart_"+chart.getId();
        //拼接prompt
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析需求：").append("\n");
        if(StrUtil.isNotBlank(chart.getChartType())){
            userGoal += "请使用" + chartType;
        }
        userInput.append(userGoal).append("\n");
        userInput.append("原始数据：").append("\n");
        userInput.append(chartData).append("\n");


        //调 ai 接口
        String result = bigModelNew.sendMessageToXingHuo(userInput.toString());
        String[] splits = result.split("'【'");
        if(splits.length < 2){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"AI异常，请重试");
        }
        String genChart = splits[1].trim();
        String genResult = splits[2].trim();

        //对分表 用动态sql进行数据更新
        List<Map<String, String>> maps = StrToMap.doMap(chartData);
        String[] updateSQLs = GetSql.buildUpdateSQL(tableName, maps);
        //获取多条update的sql一次执行
        for(String updateSQL : updateSQLs) {
            boolean b = dynamicChartMapper.updateDynamicChart(updateSQL);
            if(!b){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"原始数据分析失败，请检查后重试");
            }
        }
        //处理大模型生成结果result
        chart.setGenChart(genChart);
        chart.setGenResult(genResult);
        //修改chart 表
        boolean updateResult = this.updateById(chart);


        Chart chartInfo = chartMapper.queryUserIdByChartId(chart.getId().toString());
        if(chartInfo == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //修改时，删除修改的用户,和管理员的分页key，删除当前详细图表的key
        String key = CACHE_ID_CHART + chartInfo.getId();
        String key1 = CACHE_MY_CHART+chartInfo.getUserId()+"_*";
        String key2 = CACHE_ALL_CHART +"*";
        Set<String>  keys1 = stringRedisTemplate.keys(key1);
        Set<String>  keys2 = stringRedisTemplate.keys(key2);
        Set<String> allKeys = new HashSet<>();
        allKeys.addAll(keys2);
        allKeys.addAll(keys1);
        allKeys.add(key);
        if(allKeys != null && !allKeys.isEmpty()){
            stringRedisTemplate.delete(allKeys);
        }

        return updateResult;
    }


    @Override
    public ChartVO getChartById(long id) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        String key = CACHE_ID_CHART + id;
        //获取当前图表csv数据（原始数据）
        String tableName = "chart_"+id;
        //查询缓存是否存在，
        String chartJson = stringRedisTemplate.opsForValue().get(key);
        if(StrUtil.isNotBlank(chartJson)){
            ChartVO chartVO = JSONUtil.toBean(chartJson, ChartVO.class);
            return chartVO;
        }

        if(chartJson != null){  //存在空值
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //缓存不存，查数据库
        Chart chart = this.getById(id);
        if(chart == null){
            //缓存穿透，缓存空值
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        List<DynamicChart> dynamicEntities = dynamicChartMapper.selectListByTableName(tableName);
        //封装返回类
        ChartVO chartVO = new ChartVO();
        BeanUtils.copyProperties(chart, chartVO);
        chartVO.setDynamicData(dynamicEntities.toString());

        //真实数据写入redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(chartVO),CACHE_ID_TTL, TimeUnit.MINUTES);
        return chartVO;
    }

    /**
     * 具体用户的分页请求
     * @param chartQueryRequest
     * @param request
     * @return
     */
    @Override
    public Page<Chart> page(ChartQueryRequest chartQueryRequest, HttpServletRequest request) {

        if (chartQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        User loginUser = userService.getLoginUser(request);
        chartQueryRequest.setUserId(loginUser.getId());

        long current = chartQueryRequest.getCurrent();
        long size = chartQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        //redis 缓存key
        String key = CACHE_MY_CHART +loginUser.getId()+"_"+current;  //当前 用户id+分页页码 为key 按页码和具体用户缓存数据
        Page<Chart> chartPage = new Page<>(current,size);

        //redis缓存查询
        String chartStr = (String)stringRedisTemplate.opsForHash().get(key, "data");
        String pages = (String)stringRedisTemplate.opsForHash().get(key, "pages");
        String total = (String)stringRedisTemplate.opsForHash().get(key, "total");

        if (StrUtil.isNotBlank(chartStr)) { //缓存存在
            // 将 JSON 字符串反序列化为 List<ChartDto>
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                List<Chart> chartList = objectMapper.readValue(chartStr, new TypeReference<List<Chart>>() {
                });
                chartPage.setRecords(chartList);
                chartPage.setTotal(Long.parseLong(total));
                chartPage.setPages(Long.parseLong(pages));
                return chartPage;
            } catch (JsonProcessingException e) {
                log.error("String向List 类型转换失败", e);
                throw new RuntimeException(e);
            }
        }

        if(chartStr != null){  //错误参数，缓存为空，
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        try {
            chartPage = this.page(chartPage, getQueryWrapper(chartQueryRequest));

            //请求具体用户图表，缓存空数据，解决缓存穿透
            if(chartPage.getRecords().size() == 0){
                stringRedisTemplate.opsForHash().put(key,"data","");
                stringRedisTemplate.opsForHash().put(key,"total","");
                stringRedisTemplate.opsForHash().put(key,"pages","");
                //添加过期时间
                stringRedisTemplate.expire(key,CACHE_NULL_TTL, TimeUnit.MINUTES); //2MIN
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }

            //当查询真正存在，再写入redis
            String strList = new ObjectMapper().writeValueAsString(chartPage.getRecords());
            stringRedisTemplate.opsForHash().put(key,"data",String.valueOf(strList));
            stringRedisTemplate.opsForHash().put(key,"total",String.valueOf(chartPage.getTotal()));
            stringRedisTemplate.opsForHash().put(key,"pages",String.valueOf(chartPage.getPages()));
            //添加过期时间
            stringRedisTemplate.expire(key,CACHE_CHART_TTL, TimeUnit.MINUTES); // 3MIN
        } catch (JsonProcessingException e) {
            log.error("List向String 类型转换失败",e);
            throw new RuntimeException(e);
        }
        return chartPage;
    }



    @Override
    public Page<ChartDto> dtoPage(ChartQueryRequest chartQueryRequest) {

        if (chartQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        long current = chartQueryRequest.getCurrent();
        long size = chartQueryRequest.getPageSize();
        Page<ChartDto> chartDtoPage = new Page<>(current, size);
        //redis 缓存key
        String key = CACHE_ALL_CHART +current;  //当前分页页码为key
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        //redis缓存查询
        String chartDtoStr = (String)stringRedisTemplate.opsForHash().get(key, "data");
        String pages = (String)stringRedisTemplate.opsForHash().get(key, "pages");
        String total = (String)stringRedisTemplate.opsForHash().get(key, "total");

        if (StringUtils.isNotBlank(chartDtoStr)) { //缓存存在

            // 将 JSON 字符串反序列化为 List<ChartDto>
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                List<ChartDto> chartDtoList = objectMapper.readValue(chartDtoStr, new TypeReference<List<ChartDto>>() {
                });
                chartDtoPage.setRecords(chartDtoList);
                chartDtoPage.setTotal(Long.parseLong(total));
                chartDtoPage.setPages(Long.parseLong(pages));
                return chartDtoPage;
            } catch (JsonProcessingException e) {
                log.error("String向List 类型转换失败" + e);
                throw new RuntimeException(e);
            }
        }
        chartDtoPage = getPage(current, size, getQueryWrapper(chartQueryRequest));

        //当查询真正存在，再写入redis
        try {
            if(chartDtoPage.getRecords().size() > 0){
                String strList = new ObjectMapper().writeValueAsString(chartDtoPage.getRecords());
                stringRedisTemplate.opsForHash().put(key,"data",String.valueOf(strList));
                stringRedisTemplate.opsForHash().put(key,"total",String.valueOf(chartDtoPage.getTotal()));
                stringRedisTemplate.opsForHash().put(key,"pages",String.valueOf(chartDtoPage.getPages()));
                //设置过期时间
                stringRedisTemplate.expire(key,CACHE_CHART_TTL,TimeUnit.MINUTES);
            }
        } catch (JsonProcessingException e) {
            log.error("List向String 类型转换失败" + e);
            throw new RuntimeException(e);
        }
        return chartDtoPage;
    }


    /**
     * 查询所有用户的chart
     * @param page
     * @param pageSize
     * @param getQueryWrapper
     * @return
     */
    public Page<ChartDto> getPage(long page, long pageSize, QueryWrapper<Chart> getQueryWrapper){
        String key = CACHE_ALL_CHART +page;  //当前分页页码为key
        Page<Chart> chartPage = new Page<>(page, pageSize);
        Page<ChartDto> chartDtoPage = new Page<>(page, pageSize);

        //查询基本的图表数据
        chartPage = this.page(chartPage, getQueryWrapper);
        //拷贝对象，忽略 图表数据的实际数据
        BeanUtil.copyProperties(chartPage,chartDtoPage,"records");

        //手动赋值chartDto的 用户头像字段
        List<Chart> records = chartPage.getRecords();
        List<ChartDto> list = records.stream().map((item)->{
            ChartDto chartDto = new ChartDto();
            //拷贝基本数据
            BeanUtil.copyProperties(item,chartDto);
            //处理多表的额外数据
            Long userId = item.getUserId();
            User userInfo = userService.getById(userId);
            if(userInfo != null){
                chartDto.setUserAvatar(userInfo.getUserAvatar());
                chartDto.setUsername(userInfo.getUserName());
            }
            return chartDto;
        }).collect(Collectors.toList());

        //设置chartDto的具体值
        chartDtoPage.setRecords(list);

        return chartDtoPage;
    }

    /**
     * 获取查询图表包装类
     *
     * @param chartQueryRequest
     * @return
     */
    public QueryWrapper<Chart> getQueryWrapper(ChartQueryRequest chartQueryRequest) {
        QueryWrapper<Chart> queryWrapper = new QueryWrapper<>();
        if (chartQueryRequest == null) {
            return queryWrapper;
        }
        Long id = chartQueryRequest.getId();
        String goal = chartQueryRequest.getGoal();
        String chartType = chartQueryRequest.getChartType();
        Long userId = chartQueryRequest.getUserId();
        String charName = chartQueryRequest.getCharName();
        String sortField = chartQueryRequest.getSortField();
        String sortOrder = chartQueryRequest.getSortOrder();

        queryWrapper.eq(id != null && id > 0, "id", id);
        queryWrapper.eq(StringUtils.isNotBlank(goal), "goal", goal);
        queryWrapper.eq(StringUtils.isNotBlank(chartType), "chartType", chartType);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.like(StringUtils.isNotBlank(charName),"charName",charName);
        queryWrapper.eq("isDelete", false);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }
}




